#pragma once
#include "room.hpp"
#include <list>
#include <thread>
#include <mutex>
#include <condition_variable>

template <typename T>
class match_queue
{
public:
    void push(const T &data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.emplace_back(data);
        _cond.notify_all();
    }
    bool pop(T &data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if(_list.empty()) return false;
        data = _list.front();
        _list.pop_front();
        return true;
    }
    void remove(T &data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.remove(data);
    }
    int size()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.size();
    }
    bool empty()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.empty();
    }
    void wait()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _cond.wait(lock);
    }
private:
    std::list<T> _list;
    std::mutex _mutex;
    std::condition_variable _cond;
};

class matcher
{
public:
    matcher(room_manager *rm, User_Info_Table *tb, online_user_manager *on)
        :_room(rm), _table(tb), _online(on)
    {
        _threads.resize(9);
        _threads.emplace_back(&matcher::iron_thread_handle, this);
        _threads.emplace_back(&matcher::bronze_thread_handle, this);
        _threads.emplace_back(&matcher::silver_thread_handle, this);
        _threads.emplace_back(&matcher::gold_thread_handle, this);
        _threads.emplace_back(&matcher::platinum_thread_handle, this);
        _threads.emplace_back(&matcher::diamond_thread_handle, this);
        _threads.emplace_back(&matcher::ascendant_thread_handle, this);
        _threads.emplace_back(&matcher::immortal_thread_handle, this);
        _threads.emplace_back(&matcher::radiant_thread_handle, this);
    }
    ~matcher() 
    {
        for(auto &thread : _threads) {
            if(thread.joinable()) 
                thread.join();
        }
    }
    bool add(int uid)
    {
        // 1.根据玩家段位添加到不同的匹配队列中
        // 利用数据库句柄，根据id查询该用户的段位信息
        Json::Value user;
        if(!_table->select_by_id(uid, user)) return false;
        int ladder = user["ladder"].asInt();
        // 2.根据段位进行玩家分类
        switch(ladder) {
            case 1:
                _iron.push(uid);
                break;
            case 2:
                _bronze.push(uid);
                break;
            case 3:
                _silver.push(uid);
                break;
            case 4:
                _gold.push(uid);
                break;
            case 5:
                _platinum.push(uid);
                break;
            case 6:
                _diamond.push(uid);
                break;
            case 7:
                _ascendant.push(uid);
                break;
            case 8:
                _immortal.push(uid);
                break;
            case 9:
                _radiant.push(uid);
                break;
            default:
                _iron.push(uid);
                break;
        }
        return true;
    }
    bool del(int uid)
    {
        // 1.根据玩家段位添加到不同的匹配队列中
        // 利用数据库句柄，根据id查询该用户的段位信息
        Json::Value user;
        if(!_table->select_by_id(uid, user)) return false;
        int ladder = user["ladder"].asInt();
        // 2.根据段位进行玩家分类
        switch(ladder) {
            case 1:
                _iron.remove(uid);
                break;
            case 2:
                _bronze.remove(uid);
                break;
            case 3:
                _silver.remove(uid);
                break;
            case 4:
                _gold.remove(uid);
                break;
            case 5:
                _platinum.remove(uid);
                break;
            case 6:
                _diamond.remove(uid);
                break;
            case 7:
                _ascendant.remove(uid);
                break;
            case 8:
                _immortal.remove(uid);
                break;
            case 9:
                _radiant.remove(uid);
                break;
        }
        return true;
    }
private:
    void thread_handle(match_queue<int>& q)
    {
        while(1) {
            // 1.判断队列中是否有两个以上的人，没有则阻塞，有则继续向下判断
            while(q.size() < 2){ 
                q.wait();
            }
            // 2.队列人数大于2，取出前两个
            int uid1 = 0, uid2 = 0; 
            if(!q.pop(uid1)) { /*debug("uid1获取失败");*/continue; }
            if(!q.pop(uid2)) {// 如果出第二个时出错了，此时需要将第一个重新添加回队列中
                add(uid1);
                continue;
            }
            debug("匹配成功：uid1->%d, uid2->%d", uid1, uid2);
            // 3.都取出来了，判断两个用户连接是否正常
            auto conn1 = _online->get_conn_from_hall(uid1);
            if(conn1.get() == nullptr) { /*debug("uid1 conn获取失败");*/ add(uid2); continue; }
            auto conn2 = _online->get_conn_from_hall(uid2);
            if(conn2.get() == nullptr) { /*debug("uid2 conn获取失败");*/add(uid1); continue; }
            // 4.连接都正常，此时给这两个用户创建房间
            std::shared_ptr<room> rp = _room->create_room(uid1, uid2);
            if(rp.get() == nullptr) {
                add(uid1);
                add(uid2);
                continue;
            }
            //5. 创建房间成功，此时给两个用户发送响应
            Json::Value resp;
            std::string json_str;
            resp["optype"] = "match_success";
            resp["result"] = true;
            json_util::serialize(resp, json_str);
            printf("coun1: %p\n", conn1.get());
            printf("coun2: %p\n", conn2.get());
            conn1->send(json_str);
            conn2->send(json_str);
            debug("响应已发送 %s",json_str.c_str());
        }
    }
private:
    void iron_thread_handle() { thread_handle(std::ref(_iron)); }
    void bronze_thread_handle() { thread_handle(std::ref(_bronze)); }
    void silver_thread_handle() { thread_handle(std::ref(_silver)); }
    void gold_thread_handle() { thread_handle(std::ref(_gold)); }
    void platinum_thread_handle() { thread_handle(std::ref(_platinum)); }
    void diamond_thread_handle() { thread_handle(std::ref(_diamond)); }
    void ascendant_thread_handle() { thread_handle(std::ref(_ascendant)); }
    void immortal_thread_handle() { thread_handle(std::ref(_immortal)); }
    void radiant_thread_handle() { thread_handle(std::ref(_radiant)); }
private:
    // 不同段位的匹配队列，只有处于同一个段位的人才能匹配到一块
    match_queue<int> _iron;
    match_queue<int> _bronze;
    match_queue<int> _silver;
    match_queue<int> _gold;
    match_queue<int> _platinum;
    match_queue<int> _diamond;
    match_queue<int> _ascendant;
    match_queue<int> _immortal;
    match_queue<int> _radiant;
    std::vector<std::thread> _threads; // 不同匹配队列的线程
    room_manager *_room; // 房间管理句柄，用于为匹配成功的两个人创建房间
    User_Info_Table *_table; // 数据库句柄，需要访问指定用户的信息，才能将其放到指定的队列中
    online_user_manager *_online; // 房间匹配成功后，需要给两个用户发送匹配成功消息
};